/*
* page_capture.cc Notebook Firewire Capture Page Object
* Copyright (C) 2001 Charles Yates <charles.yates@pandora.be>
* Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org>
* Copyright (C) 2007 Stéphane Brunner <stephane.brunner@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* 
* 
   Image compositeImage( "c:\\myimages\\compositeimage.jpg" );
   Image originalImage( "c:\\myimages\\originalimage.jpg" );

   compositeImage.opacity(50);
   originalImage.composite(compositeImage,100,50,DissolveCompositeOp);
   originalImage.write("c:\\myimages\\resultimage.jpg");
*/

#include "page_capture.h"
#include "page_capture.moc"

#include <QTimer>
#include <QDebug>

#include <iostream>
using namespace std;
#include <sys/time.h>

#include <math.h>
#include <limits.h>
#include <unistd.h>

#include "displayer.h"
#include "frame.h"

/*extern "C"
{

    struct navigate_control g_nav_ctl;

    // reader changed to pointer to delay creation of Frame
    // objects so prefs dv decoder quality is properly read
    extern char cmd[];
    static char lastcmd[ 256 ];

    //
    // Preview rendering implementation
    //

    static void *captureThread( void * p );
    static void *videoThread( void *p );
    static void *mixThread( void *p );

    static pthread_t videothread;
    static pthread_t mixthread;
}*/

/**
    Constructor for page.
 
      \param common    common object to which this page belongs
*/

PageCapture::PageCapture() : lastPosition(-1), step(0)
// : displayer(NULL)// : isCapturing( false ), captureMutex( false )
{
//    QTimer *timer = new QTimer(this);
//    connect(timer, SIGNAL(timeout()), this, SLOT(mixThread()));
//    timer->start(40);

    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(videoThread()));
    timer->start(20);

    avpicture_alloc(&canalAB, PIX_FMT_RGB32, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
    avpicture_alloc(&canalOUT, PIX_FMT_RGB32, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
}

/** Destructor for page. */

PageCapture::~PageCapture()
{
    std::cerr << "> Destroying Capture Page" << std::endl;
}

void PageCapture::addInput(QWidget *target, int channel, int port) 
{
    Input *in = new Input();
    in->init(target, channel);
    in->CheckDevices(port);
    inputs.push_back(in);
//    in->start(QThread::TimeCriticalPriority);
}

void PageCapture::setScreen1(QWidget *target) {
    screen1 = FindDisplayer::getDisplayer( target, true, false );
}
void PageCapture::setScreen2(QWidget *target) {
    screen2 = FindDisplayer::getDisplayer( target, true, false );
}
void PageCapture::setScreen3(QWidget *target) {
    screen3 = FindDisplayer::getDisplayer( target, true, false );
}

void PageCapture::setABFade(float fade) {
    mixAB = fade;
}
void PageCapture::setCKeyYValue(char key) {
    mixOut = key;
}

Input::Input() : avc_enabled(false), driver_locked(false), avc(NULL)
//, framePosition(-1)
{
    avpicture_alloc(&pixels, PIX_FMT_RGB32, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
}

Input::~Input()
{
    delete reader;
    reader = NULL;
}

/** Start of page. */

void Input::init(QWidget *target, int channel)
{
    std::cerr << ">> Starting Capture" << std::endl;

    // By default, AVC is always disabled
    driver_available = false;
    check_phyid = true;
    avc_enabled = false;
    validComponents = 0;
    gui_state_was = -1;
    screen = FindDisplayer::getDisplayer( target, true, false );

    // Create the reader object
#ifdef HAVE_IEC61883
    reader = new iec61883Reader(channel);
#else
    reader = new dv1394Reader(channel);
#endif

    // Create the AV/C object
    avc = new AVC();

//    QTimer *timer = new QTimer(this);
//    connect(timer, SIGNAL(timeout()), this, SLOT(captureThread()));
//    timer->start(40);
}

/** Check devices and status.
*/

void Input::CheckDevices(int port)
{
    static int avcRetry = 0;

    if ( driver_locked == true )
    {
        std::cerr << "Stopping thread" << std::endl;
        reader->StopThread( );
        driver_locked = false;
        driver_available = false;
        avc_enabled = false;
        check_phyid = true;
        TriggerAction( );
    }
    else if ( driver_available == false )
    {
        // Start the reader thread
        //std::cerr << "Starting thread" << std::endl;
        if ( reader->StartThread( port > 0 ? port : ( avc->getPort() < 0 ) ? 0 : avc->getPort() ) )
        {
            // The drivers are installed
            driver_available = true;
            avc_enabled = false;
        }
        TriggerAction( );
    }
    else if ( check_phyid )
    {
        avc_enabled = false;
        TriggerAction( );
    }
    else if ( avc_enabled )
    {
        quadlet_t status = avc->TransportStatus( -1 );

        if ( ( int ) status < 0 )
        {
            cerr << ">>> AVC status error" << std::endl;
            if ( ++avcRetry > 2 )
            {
                avcRetry = 0;
                avc_enabled = false;
                check_phyid = true;
            }
        }
        else
        {
            char t[ 12 ];

            avcStatus = status;
            avcRetry = 0;

            TriggerAction();
        }
    }
}
/** Fetch the currently showing frame.
*/
Frame* Input::getFrame()
{
    return reader->GetFrame();
//    return framePosition < 0 ? NULL : frameBuffer[ framePosition ];
}

/*int Input::WaitForAction( int lastPosition )
{
    if ( framePosition == lastPosition )
    {
        pthread_mutex_lock( &condition_mutex );
        pthread_cond_wait( &condition, &condition_mutex );
        pthread_mutex_unlock( &condition_mutex );
    }

    return framePosition;
}*/

void Input::TriggerAction( )
{
    pthread_mutex_lock( &condition_mutex );
    pthread_cond_signal( &condition );
    pthread_mutex_unlock( &condition_mutex );
}
/*
void Input::run()
{
    Frame * frame = NULL;

    // Make sure we can tell the difference between a used and unused frame
    for ( int index = 0; index < BUFFERED_FRAMES; index ++ ) {
        frameBuffer[ index ] = NULL;
    }
    
    // Loop until we're informed otherwise
    while ( true )
    {
        // Wait for the reader to indicate that something has happened
        reader->WaitForAction();

        // Get the next frame
        frame = reader->GetFrame();

        // Put the last collected frame into the buffer so it can be picked up
        // by the video thread.
        if ( frame != NULL && frame->GetWidth() > 0 && frame->GetHeight() > 0 )
        {
            // Bung it in to the buffer
            int newest = ( framePosition + 1 ) % BUFFERED_FRAMES;
            if (frameBuffer[ newest ] != NULL) {
                delete frameBuffer[ newest ];
            }
            frameBuffer[ newest ] = frame;
            framePosition = newest;
            TriggerAction();
        }
    }

    // Set the buffer positional vars to defaults to allow restart
//        frameCount = 0;
    framePosition = -1;
    

//    g_nav_ctl.capture_active = FALSE;

    TriggerAction();
}*/

void PageCapture::videoThread()
{
//qDebug()<<"PageCapture::videoThread";
        int size = inputs.size();
        switch (step % 4) {
            case 0:
                if (size > inputOnA && inputs[inputOnA] != NULL && screen1 != NULL) 
                {
                    screen1->put(&inputs[inputOnA]->pixels, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
                }
                break;

            case 1:
                if (size > inputOnB && inputs[inputOnB] != NULL && screen2 != NULL) 
                {
                    screen2->put(&inputs[inputOnB]->pixels, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
                }
                break;

            case 2:
                AVPicture *pixel;
                switch (onScreen3) {
                    case C:
                        pixel = &inputs[inputOnC]->pixels;
                        break;
                    case AB:
                        pixel = &canalAB;
                        break;
                    case OUT:
                        pixel = &canalOUT;
                        break;
                }
                if (pixel != NULL && screen3 != NULL)
                {
                    screen3->put(pixel, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
                }
                break;

            default:
                if ((step/4) != inputOnA && (step/4) != inputOnB && (step/4) != inputOnC)
                {
                    inputs[step/4]->getFrame()->ExtractPreviewRGB(&inputs[step/4]->pixels, false); // 33ms
                }
qDebug()<<inputs[step/4]->pixels.data[0][2];
                inputs[step/4]->screen->put(&inputs[step/4]->pixels, FRAME_MAX_WIDTH, FRAME_MAX_HEIGHT);
                break;
        }


        step = inputs.size() == 0 ? (step + 1) % 3 : (step + 1) % (inputs.size() * 4);
}

void PageCapture::run()
{
//
    while ( true )
    {
        /// add last read on all channel 
        int size = inputs.size();
        if (size > inputOnA && size > inputOnB && size > inputOnC) {
struct timeval start;
gettimeofday(&start, NULL);
            if (inputs[inputOnA] != NULL) {
                Frame *frame = inputs[inputOnA]->getFrame();
                if (frame != NULL) {
                    frame->ExtractPreviewRGB(&inputs[inputOnA]->pixels, false); // 20ms
                }
            }
{
struct timeval end;
gettimeofday(&end, NULL);
//cerr<<(end.tv_sec - start.tv_sec) * 1000.0 + (end.tv_usec - start.tv_usec) / 1000.0<<endl; // 720x576 => 40ms
}
            if (inputs[inputOnB] != NULL) {
                Frame *frame = inputs[inputOnB]->getFrame();
                if (frame != NULL) {
                    frame->ExtractPreviewRGB(&inputs[inputOnB]->pixels, false); // 20ms
                }
            }
            if (inputs[inputOnC] != NULL) {
                Frame *frame = inputs[inputOnC]->getFrame();
                if (frame != NULL) {
                    frame->ExtractPreviewRGB(&inputs[inputOnC]->pixels, false); // 20ms
                }
            }
        }

//        void *canalA;
//        void *canalB;
//        void *canalC;
//        void *canalAB;
//        void *canalOUT;
//    
//        /* mix data */
//        int inputOnA;
//        int inputOnB;
//        int inputOnC;
//    
//        float mixAB;
//        float mixABC;
//        }

        // Release temporarily
        struct timespec t = { 0, 40*1000*1000 }; // 40ms
        nanosleep( &t, NULL );
    }
}
